﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data.Common;
using System.Configuration;
using System.Transactions;

namespace Sinacor.Infra.Tools.LanguageImporter
{
    public class ExcelImporter
    {
        protected ExcelImporter()
        {

        }

        protected static ExcelImporter _instance = null;
        protected static object _lock = new object();

        public static ExcelImporter GetInstance()
        {
            lock (_lock)
            {
                if (_instance == null)
                {
                    _instance = new ExcelImporter();
                }

                return _instance;
            }
        }

        public void Import(string path, string sheetName, string language)
        {
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");
            if (string.IsNullOrEmpty(sheetName))
                throw new ArgumentNullException("sheetName");
            if (string.IsNullOrEmpty(language))
                throw new ArgumentNullException("language");

            if (Path.GetExtension(path).ToLower() != ".xlsx")
                throw new DirectoryNotFoundException(string.Format("The file \"{0}\" is not an Excel sheet", path));

            if (!File.Exists(path))
                throw new DirectoryNotFoundException(string.Format("The file \"{0}\" doesn't exist", path));

            if (language != "pt-BR" &&
                language != "en-US" &&
                language != "es-ES")
                throw new ArgumentException(string.Format("Invalid language \"{0}\"", language), "language");


            int lineNumber = 1, sucessCount = 0;

            ShowInformation("Importing...\n");
            ShowInformation("File:     {0}", path);
            ShowInformation("Sheet:    {0}", sheetName);
            ShowInformation("Language: {0}\n", language);

            DbProviderFactory excelFactory = DbProviderFactories.GetFactory("System.Data.OleDb");

            using (DbConnection excelConn = excelFactory.CreateConnection())
            {
                excelConn.ConnectionString = string.Format(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=""{0}"";Extended Properties=""Excel 12.0;HDR=YES;""", path);
                excelConn.Open();

                using (DbCommand excelComm = excelConn.CreateCommand())
                {
                    excelComm.CommandText = string.Format("SELECT * FROM [{0}$]", sheetName);

                    using (DbDataReader excelReader = excelComm.ExecuteReader())
                    {
                        DbProviderFactory oracleFactory = DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings["SINATOOLS"].ProviderName);

                        using (DbConnection oracleConn = oracleFactory.CreateConnection())
                        {
                            oracleConn.ConnectionString = ConfigurationManager.ConnectionStrings["SINATOOLS"].ConnectionString;
                            oracleConn.Open();

                            using (DbCommand oracleComm = oracleConn.CreateCommand())
                            {
                                DbParameter codigoMensagemParam = oracleComm.CreateParameter();
                                codigoMensagemParam.ParameterName = "CodigoMensagem";
                                codigoMensagemParam.DbType = System.Data.DbType.Int32;

                                DbParameter mensagemParam = oracleComm.CreateParameter();
                                mensagemParam.ParameterName = "Mensagem";
                                mensagemParam.DbType = System.Data.DbType.String;

                                DbParameter codigoTxtParam = oracleComm.CreateParameter();
                                codigoTxtParam.ParameterName = "CodigoTxt";
                                codigoTxtParam.DbType = System.Data.DbType.Int32;

                                int codigoMensagem;
                                string mensagem;

                                while (excelReader.Read())
                                {
                                    codigoMensagem = 0;
                                    mensagem = null;
                                    lineNumber++;

                                    try
                                    {
                                        // Capturando código e mensagem
                                        codigoMensagem = (int)(!excelReader.IsDBNull(0) ? excelReader.GetDouble(0) : -1);
                                        mensagem = (!excelReader.IsDBNull(2) ? excelReader.GetString(2) : null);

                                        // Código não pode ser nulo
                                        if (codigoMensagem == -1)
                                            continue;

                                        // Mensagem não pode ser vazia
                                        if (string.IsNullOrEmpty(mensagem))
                                        {
                                            ShowError(lineNumber, codigoMensagem, mensagem, "The message cannot be empty");
                                            continue;
                                        }
                                       
                                        // Verifica se o código é valido
                                        string sql = @"
                                            SELECT 1 
                                            FROM 
                                                TFWDMENSAGEM M 
                                            WHERE 
                                                M.CODIGOMENSAGEM = :CodigoMensagem";

                                        oracleComm.CommandText = sql;
                                        oracleComm.Parameters.Clear();

                                        codigoMensagemParam.Value = codigoMensagem;
                                        oracleComm.Parameters.Add(codigoMensagemParam);

                                        if (oracleComm.ExecuteScalar() == null)
                                        {
                                            ShowError(lineNumber, codigoMensagem, mensagem, "The message id doesn't exist");
                                            continue;
                                        }

                                        // Substituindo aspas
                                        mensagem = mensagem.Replace('“', '"').Replace('”', '"');

                                        // Tenta capturar código txt da mensagem
                                        sql = string.Format(@"
                                            SELECT M.COD_TXT_MSG 
                                            FROM 
                                                TFWDTRADUCAOMENSAGEM T
                                                INNER JOIN TFWDTXT_MSG M ON 
                                                    T.COD_TXT_MSG = M.COD_TXT_MSG
                                            WHERE 
                                                T.CODIGOMENSAGEM = :CodigoMensagem AND 
                                                M.CODIGOIDIOMA = (SELECT CODIGOIDIOMA FROM TFWDIDIOMA WHERE SIGLAIDIOMA = '{0}')", language);

                                        oracleComm.CommandText = sql;
                                        oracleComm.Parameters.Clear();

                                        codigoMensagemParam.Value = codigoMensagem;
                                        oracleComm.Parameters.Add(codigoMensagemParam);

                                        int codigoTxt = Convert.ToInt32(oracleComm.ExecuteScalar() ?? -1);

                                        using (TransactionScope transaction = new TransactionScope())
                                        {
                                            if (codigoTxt == -1)
                                            {
                                                // Se mensagem não existir insere
                                                sql = @"SELECT STFWDTXT_MSG.NEXTVAL FROM DUAL";

                                                oracleComm.CommandText = sql;
                                                oracleComm.Parameters.Clear();

                                                codigoTxt = Convert.ToInt32(oracleComm.ExecuteScalar());


                                                sql = string.Format(@"
                                                    INSERT INTO TFWDTXT_MSG (
                                                        COD_TXT_MSG, 
                                                        CODIGOIDIOMA, 
                                                        TXT_MSG, 
                                                        COD_USUA_LOGN
                                                    ) VALUES (
                                                        :CodigoTxt, 
                                                        (SELECT CODIGOIDIOMA FROM TFWDIDIOMA WHERE SIGLAIDIOMA = '{0}'), 
                                                        :Mensagem, 
                                                        ' '
                                                    )", language);

                                                oracleComm.CommandText = sql;
                                                oracleComm.Parameters.Clear();

                                                codigoTxtParam.Value = codigoTxt;
                                                oracleComm.Parameters.Add(codigoTxtParam);

                                                mensagemParam.Value = mensagem;
                                                oracleComm.Parameters.Add(mensagemParam);

                                                oracleComm.ExecuteNonQuery();


                                                sql = @"
                                                    INSERT INTO TFWDTRADUCAOMENSAGEM (
                                                        CODIGOMENSAGEM, 
                                                        COD_TXT_MSG, 
                                                        CODIGOSTATUSTRADUCAO
                                                    ) VALUES (
                                                        :CodigoMensagem, 
                                                        :CodigoTxt, 
                                                        4
                                                    )";

                                                oracleComm.CommandText = sql;
                                                oracleComm.Parameters.Clear();

                                                codigoMensagemParam.Value = codigoMensagem;
                                                oracleComm.Parameters.Add(codigoMensagemParam);

                                                codigoTxtParam.Value = codigoTxt;
                                                oracleComm.Parameters.Add(codigoTxtParam);

                                                oracleComm.ExecuteNonQuery();
                                            }
                                            else
                                            {
                                                // Se mensagem já existir atualiza
                                                sql = @"
                                                    UPDATE TFWDTXT_MSG SET 
                                                        TXT_MSG = :Mensagem
                                                    WHERE COD_TXT_MSG = :CodigoTxt";

                                                oracleComm.CommandText = sql;
                                                oracleComm.Parameters.Clear();

                                                codigoTxtParam.Value = codigoTxt;
                                                oracleComm.Parameters.Add(codigoTxtParam);

                                                mensagemParam.Value = mensagem;
                                                oracleComm.Parameters.Add(mensagemParam);

                                                oracleComm.ExecuteNonQuery();
                                            }

                                            transaction.Complete();
                                            sucessCount++;
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        ShowError(lineNumber, codigoMensagem, mensagem, ex);
                                    }
                                }


                            }
                        }


                    }
                }
            }


            ShowSuccess("{0} lines imported successfully", sucessCount);
        }

        public event EventHandler<StatusEventArgs> Status;

        protected void OnStatus(StatusEventArgs e)
        {
            if (Status != null)
                Status(this, e);
        }

        protected void ShowInformation(string message, params object[] args)
        {
            OnStatus(new StatusEventArgs(string.Format(message, args)));
        }
        protected void ShowStep(string message, params object[] args)
        {
            OnStatus(new StatusEventArgs(StatusType.Step, string.Format(message, args)));
        }
        protected void ShowSuccess(string message, params object[] args)
        {
            OnStatus(new StatusEventArgs(StatusType.Success, string.Format(message, args)));
        }
        protected void ShowError(int lineNumber, int messageId, string message, Exception error)
        {
            OnStatus(new StatusEventArgs(new Exception(string.Format("Error on line {0}: \"{1} - {2}\"", lineNumber, messageId, message), error)));
        }
        protected void ShowError(int lineNumber, int messageId, string message, string error, params object[] args)
        {
            OnStatus(new StatusEventArgs(StatusType.Error, string.Format("Error on line {0}: \"{1} - {2}\"\n{3}", lineNumber, messageId, message, string.Format(error, args))));
        }
    }
}
